package handler

import (
	"context"
	"fmt"
	"go_final/mxshop_srvs/inventory_srv/global"
	"go_final/mxshop_srvs/inventory_srv/model"
	inventorypb "go_final/mxshop_srvs/inventory_srv/proto/protofile/inventory"
	"sync"

	"github.com/go-redsync/redsync/v4"
	"github.com/go-redsync/redsync/v4/redis/goredis/v8"

	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"gorm.io/gorm"

	"google.golang.org/protobuf/types/known/emptypb"
)

type Server struct{}

func (s Server) SetInventory(c context.Context, info *inventorypb.GoodsInvInfo) (*emptypb.Empty, error) {
	// TODO 需要调用商品服务看该商品存不存在
	var inventory model.Inventory
	res := global.DB.Where(&model.Inventory{
		GoodsID: info.GoodsID,
	}).First(&inventory)
	if res.Error != nil && res.Error != gorm.ErrRecordNotFound {
		return nil, status.Error(codes.Internal, res.Error.Error())
	}
	inventory.GoodsID = info.GoodsID
	inventory.Stocks = info.Num

	res = global.DB.Save(&inventory)
	if res.Error != nil && res.Error != gorm.ErrRecordNotFound {
		return nil, status.Error(codes.Internal, res.Error.Error())
	}

	return &emptypb.Empty{}, nil
}

func (s Server) InventoryDetail(c context.Context, info *inventorypb.GoodsInvInfo) (*inventorypb.GoodsInvInfo, error) {
	var inventory model.Inventory
	res := global.DB.Where(&model.Inventory{
		GoodsID: info.GoodsID,
	}).First(&inventory)

	if res.Error != nil && res.Error != gorm.ErrRecordNotFound {
		return nil, status.Error(codes.Internal, res.Error.Error())
	}

	if res.RowsAffected == 0 {
		return nil, status.Error(codes.NotFound, "没有该商品库存")
	}

	return &inventorypb.GoodsInvInfo{
		GoodsID: inventory.GoodsID,
		Num:     inventory.Stocks,
	}, nil
}

var m sync.Mutex

func (s Server) Sell(c context.Context, info *inventorypb.SellInfo) (*emptypb.Empty, error) {
	// 开启事务
	// 并发情况之下, 可能会出现超卖
	tx := global.DB.Begin()

	var pool = goredis.NewPool(global.Redis)
	var rs = redsync.New(pool)

	// 获取同步锁
	// m.Lock()
	sellDetail := model.StocksSellDetail{
		OrderSn: info.OrderSn,
		Status:  1,
	}
	var details []model.GoodsDetail

	for _, goodsInfo := range info.GoodsInfo {
		details = append(details, model.GoodsDetail{
			Goods: goodsInfo.GoodsID,
			Num:   goodsInfo.Num,
		})

		// 获取redis锁
		mutex := rs.NewMutex(fmt.Sprintf("redLock_for_goods_%d", goodsInfo.GoodsID))
		if err := mutex.Lock(); err != nil {
			return nil, status.Error(codes.Internal, err.Error())
		}

		var inv model.Inventory
		// 查询库存信息
		// 悲观锁
		//res := tx.Clauses(clause.Locking{
		//	Strength: "UPDATE",
		//}).Where(&model.Inventory{
		//	GoodsID: goodsInfo.GoodsID,
		//}).First(&inv)

		res := global.DB.Where(&model.Inventory{
			GoodsID: goodsInfo.GoodsID,
		}).First(&inv)

		if res.Error != nil && res.Error != gorm.ErrRecordNotFound {
			fmt.Println("res.Error === >", res.Error.Error())
			tx.Rollback()
			return nil, status.Error(codes.Internal, res.Error.Error())
		}

		if res.RowsAffected == 0 {
			tx.Rollback()
			return nil, status.Error(codes.NotFound, "没有该商品库存")
		}

		version := inv.Version

		if inv.Stocks < goodsInfo.Num {
			tx.Rollback()
			return nil, status.Error(codes.ResourceExhausted, "库存不足")
		}

		// 开始扣减库存
		res = tx.Model(&model.Inventory{}).Where(&model.Inventory{
			GoodsID: goodsInfo.GoodsID,
			Version: version,
		}).Updates(map[string]interface{}{
			"stocks":  inv.Stocks - goodsInfo.Num,
			"version": inv.Version + 1,
		})

		//res = tx.Model(&model.Inventory{}).Where("goods_id = ? and version = ?", goodsInfo.GoodsID,
		//	version).Updates(model.Inventory{
		//	Stocks:  inv.Stocks - goodsInfo.Num,
		//	Version: inv.Version + 1,
		//})

		if res.Error != nil && res.Error != gorm.ErrRecordNotFound {
			fmt.Println("Error", res.Error.Error())
			tx.Rollback()
			return nil, status.Error(codes.Internal, res.Error.Error())
		}

		if res.RowsAffected == 0 {
			tx.Rollback()
			return nil, status.Error(codes.Internal, "扣减库存失败")
		}

		// 释放分布式锁
		if ok, err := mutex.Unlock(); !ok || err != nil {
			return nil, status.Error(codes.Internal, err.Error())
		}
	}

	sellDetail.Detail = details
	// 写入sellDetail表
	res := tx.Create(&sellDetail)
	if res.Error != nil {
		tx.Rollback()
		return nil, status.Error(codes.Internal, res.Error.Error())
	}
	if res.RowsAffected == 0 {
		tx.Rollback()
		return nil, status.Error(codes.Internal, "保存库存扣减信息失败")
	}

	tx.Commit()

	// 释放同步锁
	// m.Unlock()

	return &emptypb.Empty{}, nil
}

// Reback 库存归还: 1.订单超时归还	2.创建订单失败, 归还之前扣减的库存 3.手动归还
func (s Server) Reback(c context.Context, info *inventorypb.SellInfo) (*emptypb.Empty, error) {
	tx := global.DB.Begin()

	for _, goodsInfo := range info.GoodsInfo {
		var inv model.Inventory
		// 查询库存信息
		res := global.DB.Where(&model.Inventory{
			GoodsID: goodsInfo.GoodsID,
		}).First(&inv)
		if res.Error != nil && res.Error != gorm.ErrRecordNotFound {
			tx.Rollback()
			return nil, status.Error(codes.Internal, res.Error.Error())
		}

		if res.RowsAffected == 0 {
			tx.Rollback()
			return nil, status.Error(codes.NotFound, "没有该商品库存")
		}

		// 开始扣减库存
		inv.Stocks += goodsInfo.Num
		res = tx.Save(&inv)
		if res.Error != nil && res.Error != gorm.ErrRecordNotFound {
			tx.Rollback()
			return nil, status.Error(codes.Internal, res.Error.Error())
		}
	}

	tx.Commit()
	return &emptypb.Empty{}, nil
}
